package com.meida.module.user.provider.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.meida.common.base.service.BaseAppUserService;
import com.meida.common.base.service.BaseImService;
import com.meida.common.base.utils.FlymeUtils;
import com.meida.common.constants.CommonConstants;
import com.meida.common.constants.QueueConstants;
import com.meida.common.enums.AccountTypeEnum;
import com.meida.common.enums.StateEnum;
import com.meida.common.mybatis.base.service.impl.BaseServiceImpl;
import com.meida.common.base.entity.EntityMap;
import com.meida.common.mybatis.model.*;
import com.meida.common.mybatis.query.CriteriaDelete;
import com.meida.common.mybatis.query.CriteriaQuery;
import com.meida.common.mybatis.query.CriteriaSave;
import com.meida.common.mybatis.query.CriteriaUpdate;
import com.meida.common.security.OpenHelper;
import com.meida.common.security.OpenUser;
import com.meida.common.utils.*;
import com.meida.module.system.provider.service.SysAreaService;
import com.meida.module.user.client.entity.AppAccount;
import com.meida.module.user.client.entity.AppRole;
import com.meida.module.user.client.entity.AppUser;
import com.meida.module.user.provider.mapper.AppUserMapper;
import com.meida.module.user.provider.service.AppAccountService;
import com.meida.module.user.provider.service.AppRoleService;
import com.meida.module.user.provider.service.AppUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

/**
 * @author: zyf
 * @date: 2018/10/24 16:33
 * @description:
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class AppUserServiceImpl extends BaseServiceImpl<AppUserMapper, AppUser> implements AppUserService, BaseAppUserService {

    @Resource
    private RedisTokenStore redisTokenStore;

    @Resource
    private AppRoleService roleService;


    @Resource
    private AppUserMapper appUserMapper;
    @Resource
    private AutowireCapableBeanFactory spring;

    @Resource
    private AppAccountService appAccountService;

    @Resource
    private SysAreaService areaService;

    @Autowired(required = false)
    private BaseImService imHander;


    @Override
    public ResultBody beforePageList(CriteriaQuery<AppUser> cq, AppUser appUser, EntityMap requestMap) {
        cq.select(AppUser.class, "*");
        cq.likeByField(AppUser.class, "userName");
        cq.likeByField(AppUser.class, "nickName");
        cq.ge(AppUser.class, "createTime", cq.getParams("beginDate"));
        cq.le(AppUser.class, "createTime", cq.getParams("endDate"));
        cq.eq(AppUser.class, "userType");
        cq.eq(AppUser.class, "mobile");
        cq.like(AppUser.class, "birthday");
        cq.eq(AppUser.class, "status");
        cq.eq(AppUser.class, "deptId");
        cq.eq(AppUser.class, "positionId");
        return ResultBody.ok();
    }

    /**
     * 依据系统用户Id查询系统用户信息
     *
     * @param userId
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public AppUser getUserById(Long userId) {
        return appUserMapper.selectById(userId);
    }


    /**
     * 依据系统用户Id查询系统用户信息
     *
     * @param userId
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public AppUser getUserByShareCode(String shareCode) {
        CriteriaQuery cq = new CriteriaQuery(AppUser.class);
        cq.eq(true, "shareCode", shareCode);
        return getOne(cq);
    }

    /**
     * 获取当前用户详细信息
     *
     * @param model
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public ResultBody getMineInfo(Map params) {
        Long userId = OpenHelper.getUserId();
        CriteriaQuery<EntityMap> cq = new CriteriaQuery(params, AppUser.class);
        cq.select(AppUser.class, "*");
        cq.eq(AppUser.class, "userId", userId);
        ResultBody<EntityMap> resultBody = baseGet(cq);
        return resultBody;
    }


    /**
     * 获取当前用户详细信息
     *
     * @param model
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public List<EntityMap> selectUsers(String userIds) {
        List<EntityMap> list = new ArrayList<>();
        if (FlymeUtils.isNotEmpty(userIds)) {
            String[] userIdArray = userIds.split(",");
            CriteriaQuery<EntityMap> cq = new CriteriaQuery(AppUser.class);
            cq.select(AppUser.class, "userId", "nickName", "avatar");
            cq.in("userId", userIdArray);
            list = selectEntityMap(cq);
        }
        return list;
    }


    /**
     * 依据登录名查询系统用户信息
     *
     * @param username
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public AppUser getUserByUsername(String username) {
        QueryWrapper<AppUser> queryWrapper = new QueryWrapper();
        queryWrapper.lambda().eq(AppUser::getUserName, username);
        AppUser saved = appUserMapper.selectOne(queryWrapper);
        return saved;
    }


    /**
     * 依据手机号查询用户
     *
     * @param mobile
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public AppUser getUserByMobile(String mobile) {
        if (ObjectUtils.isNotEmpty(mobile)) {
            QueryWrapper<AppUser> queryWrapper = new QueryWrapper();
            queryWrapper.lambda().eq(true, AppUser::getMobile, mobile);
            return getOne(queryWrapper);
        } else {
            return null;
        }

    }


    /**
     * APP登录初始化用户数据
     *
     * @return
     */
    @Override
    public ResultBody userInit(Map params) {
        OpenUser user = OpenHelper.getUser();
        Long userId = Optional.ofNullable(user.getUserId()).orElse(null);
        CriteriaQuery<EntityMap> cq = new CriteriaQuery(params, AppUser.class);
        cq.select(AppUser.class, "*");
        cq.eq(AppUser.class, "userId", userId);
        ResultBody resultBody = baseGet(cq);
        if (resultBody.isOk()) {
            AppAccount appAccount = appAccountService.getMobileAccount(userId);
            EntityMap result = (EntityMap) resultBody.getData();
            result.put("accountId", user.getAccountId());
            result.put("accountType", user.getAccountType());
            result.put("userId", userId);
            if (FlymeUtils.isNotEmpty(appAccount)) {
                result.put("mobileAccount", appAccount.getAccount());
            }
            resultBody.data(result);
        }
        return resultBody;
    }

    private ResultBody initUser(Long userId) {
        OpenUser user = OpenHelper.getUser();
        CriteriaQuery<EntityMap> cq = new CriteriaQuery(AppUser.class);
        cq.select(AppUser.class, "*");
        cq.eq(AppUser.class, "userId", userId);
        ResultBody resultBody = baseGet(cq);
        if (resultBody.isOk()) {
            EntityMap result = (EntityMap) resultBody.getData();
            result.put("accountId", user.getAccountId());
            result.put("accountType", user.getAccountType());
            result.put("userId", userId);
            resultBody.data(result);
        }
        return resultBody;
    }

    /**
     * 綁定手机号
     */
    @Override
    public ResultBody<AppUser> bingMobile(String mobile, String smsCode, String passowrd, String userType) {
        Long accountId = OpenHelper.getAccountId();
        ApiAssert.isNotEmpty("账户ID异常", accountId);
        redisUtils.validSmsCode(mobile, smsCode);
        //需要绑定的用户
        AppUser appUser = null;
        //获取当前账户
        AppAccount account = appAccountService.getById(accountId);

        //当手机号已注册未绑定时获取手机号账户
        AppAccount mobileAccount = appAccountService.getMobileAccount(mobile);


        if (ObjectUtil.isNotNull(mobileAccount)) {

            appUser = getUserById(mobileAccount.getUserId());
        }
        Long userId = account.getUserId();
        if (FlymeUtils.isNotEmpty(userId)) {
            appUser = getUserById(userId);
        }
        if (ObjectUtils.isEmpty(appUser)) {
            appUser = new AppUser();
            //推送扩展事件
            appUser = (AppUser) amqpTemplate.convertSendAndReceive(QueueConstants.QUEUE_BIND_MOBILE, appUser);
            appUser.setMobile(mobile);
            //推送扩展事件
            appUser.setNickName(account.getNickName());
            appUser.setAvatar(account.getAvatar());
            if (FlymeUtils.isNotEmpty(userType) && FlymeUtils.isEmpty(appUser.getUserType())) {
                appUser.setUserType(userType);
            }
            initAppUser(appUser);
            this.save(appUser);
        }

        if (ObjectUtil.isNull(mobileAccount)) {
            appAccountService.registerMobileAccount(appUser.getUserId(), mobile, passowrd, true);
        }
        OpenUser openUser = OpenHelper.getUser();
        openUser.setUserId(appUser.getUserId());
        //更新openUser
        OpenHelper.updateOpenUser(redisTokenStore, openUser);
        //关联userId
        UpdateWrapper u = new UpdateWrapper();
        appAccountService.update(u.set(true, "userId", appUser.getUserId()).eq(true, "accountId", account.getAccountId()));
        ResultBody resultBody = initUser(appUser.getUserId());
        return resultBody.setMsg("绑定成功");

    }

    /**
     * 换绑手机号
     */
    @Override
    public ResultBody<Boolean> changeMobile(Long userId, String mobile, String smsCode) {
        AppAccount appAccount = appAccountService.getMobileAccount(userId);
        ApiAssert.isNotEmpty("账户不存在", appAccount);
        ApiAssert.isFalse("手机号已绑定其他账户", appAccountService.checkByAccountName(mobile));
        redisUtils.validSmsCode(mobile, smsCode);
        UpdateWrapper u = new UpdateWrapper();
        appAccountService.update(u.set(true, "account", mobile).eq(true, "accountId", appAccount.getAccountId()));
        UpdateWrapper user = new UpdateWrapper();
        update(user.set(true, "mobile", mobile).eq(true, "userID", userId));
        return ResultBody.ok("更换成功", mobile);
    }


    @Override
    public void bindCompany(OpenUser openUser) {
        Long userId = openUser.getUserId();
        UpdateWrapper u = new UpdateWrapper();
        //更新openUser
        OpenHelper.updateOpenUser(redisTokenStore, openUser);
        update(u.set(true, "companyId", openUser.getCompanyId()).eq(true, "userId", userId));
    }


    @Override
    public void unBindCompany(Long companyId) {
        OpenUser openUser = OpenHelper.getUser();
        Long userId = openUser.getUserId();
        UpdateWrapper u = new UpdateWrapper();
        openUser.setCompanyId(companyId);
        //更新openUser
        OpenHelper.updateOpenUser(redisTokenStore, openUser);
        update(u.set(true, "companyId", null).eq(true, "userId", userId));
    }

    @Override
    public AppUser save(String usreName, String mobile, Long companyId) {
        AppUser appUser = new AppUser();
        appUser.setUserName(usreName);
        appUser.setMobile(mobile);
        appUser.setCompanyId(companyId);
        save(appUser);
        return appUser;
    }

    @Override
    public ResultBody saveUser(Map model) {
        //用户名账户
        CriteriaSave cs = new CriteriaSave(model, AppUser.class);
        String accountName = cs.getParams("account");
        String password = cs.getParams("password");
        ApiAssert.isNotEmpty("账户名不能为空", accountName);
        ResultBody resultBody = baseAdd(cs, cs.getEntity(AppUser.class));
        //用户保存成功后注册账户
        if (resultBody.isOk()) {
            AppUser appUser = (AppUser) resultBody.getData();
            //注册账户信息
            regAccount(accountName, "+86", "-1", password, appUser);
        }
        return resultBody;
    }

    @Override
    public Integer countByInviterId(Long userId) {
        CriteriaQuery cq = new CriteriaQuery(AppUser.class);
        cq.eq(true, "inviterId", userId);
        return count(cq);
    }


    private void regAccount(String accountName, String areaCode, String smsCode, String password, AppUser appUser) {
        if (FlymeUtils.isNotEmpty(accountName)) {
            if (StringUtils.matchEmail(accountName)) {
                //注册邮箱账户
                appAccountService.registerEmailAccount(appUser.getUserId(), accountName, password);
            } else if (StringUtils.isPhoneLegal(accountName, areaCode)) {
                //校验验证码
                redisUtils.validSmsCode(accountName, smsCode);
                //注册手机号账户
                appAccountService.registerMobileAccount(appUser.getUserId(), accountName, password, true);
            } else {
                //注册用户名账户
                appAccountService.registerUsernameAccount(appUser.getUserId(), accountName, password, true);
            }
        }
    }

    /**
     * 注册账号前置处理
     */
    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public ResultBody beforeAdd(CriteriaSave cs, AppUser appUser, EntityMap extra) {
        String telephone = cs.getParams("telephone");
        String accountName = cs.getParams("accountName");
        String handlerName = cs.getParams("handlerName");
        String userType = cs.getParams("userType", "USER");
        cs.setHandlerName(handlerName);
        ApiAssert.anyOneIsNotNull("请至少填写一个账户", telephone, accountName);
        appUser.setUserType(userType);
        String email = cs.getParams("email");
        if (StringUtils.matchEmail(accountName)) {
            email = accountName;
        }
        appUser.setEmail(email);
        appUser.setNickName(cs.getParams("nickName"));
        AppRole role = roleService.getByRoleCode(cs.getParams("roleCode", "USER"));
        if (FlymeUtils.isNotEmpty(role)) {
            appUser.setRoleId(role.getRoleId());
        }
        //推荐人检验
        String inviter = cs.getParams("inviter");
        if (FlymeUtils.isNotEmpty(inviter)) {
            AppUser inviterUser = null;
            if (StringUtils.matchMobile(inviter)) {
                inviterUser = getUserByMobile(inviter);
            }
            if (FlymeUtils.isEmpty(inviterUser) && FlymeUtils.isNotEmpty(inviter)) {
                inviterUser = getUserByShareCode(inviter);
            }
            ApiAssert.isNotEmpty("推荐人不存在", inviterUser);
            if (ObjectUtils.isNotEmpty(inviterUser)) {
                //设置推荐人
                appUser.setInviterId(inviterUser.getUserId());
            }
        }
        if (FlymeUtils.isNotEmpty(telephone)) {
            appUser.setMobile(telephone);
        }
        initAppUser(appUser);
        return ResultBody.ok();
    }

    /**
     * 注册账号后置处理
     */
    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public ResultBody afterAdd(CriteriaSave cs, AppUser appUser, EntityMap extra) {
        String accountName = cs.getParams("accountName");
        String areaCode = cs.getParams("areaCode");
        String smsCode = cs.getParams("smsCode", "+86");
        String password = cs.getParams("password", CommonConstants.DEF_PWD);
        String telephone = cs.getParams("telephone");
        //注册账户信息
        if (FlymeUtils.isNotEmpty(accountName)) {
            regAccount(accountName, areaCode, smsCode, password, appUser);
        }

        if (FlymeUtils.isNotEmpty(telephone) && !StringUtils.matchMobile(accountName)) {
            regAccount(telephone, areaCode, smsCode, password, appUser);
        }
        cs.put("userId", appUser.getUserId());
        //推送注册成功通知
        amqpTemplate.convertAndSend(QueueConstants.QUEUE_USER_REG, cs);
        return ResultBody.msg("注册成功");
    }

    /**
     * 注册账号(手机号登录直接注册)
     *
     * @param accountName
     * @param password
     * @return
     */
    @Override
    public void registerByMobile(String accountName, String password, String userType) {
        String avatar = CommonConstants.DEFAULT_USERHEAD;
        String accontType = AccountTypeEnum.MOBILE.getCode();
        AppAccount appAccount = appAccountService.getUserAccount(accountName, accontType);
        if (ObjectUtils.isEmpty(appAccount)) {
            AppUser appUser = new AppUser();
            initAppUser(appUser);
            appUser.setUserType(userType);
            appUser.setMobile(accountName);
            save(appUser);
            appAccountService.registerMobileAccount(appUser.getUserId(), accountName, null, false);
            Map map = new HashMap();
            map.put("userId", appUser.getUserId());
            map.put("userType", userType);
            map.put("accountName", accountName);
            map.put("password", password);
            CriteriaSave cs = new CriteriaSave(map, AppUser.class);
            //推送注册成功通知
            amqpTemplate.convertAndSend(QueueConstants.QUEUE_USER_REG, cs);
        }
    }

    /**
     * 初始化用户信息
     *
     * @param appUser
     */
    private void initAppUser(AppUser appUser) {
        Long userId = IdWorker.getId();
        appUser.setUserId(userId);
        appUser.setStatus(CommonConstants.ENABLED);
        //生成邀请码
        appUser.setShareCode(ShareCodeUtil.toSerialCode(userId));
        if (FlymeUtils.isEmpty(appUser.getNickName())) {
            appUser.setNickName("游客" + RandomUtil.randomNumbers(5));
        }
        if (FlymeUtils.isEmpty(appUser.getAvatar())) {
            appUser.setAvatar(CommonConstants.DEFAULT_USERHEAD);
        }
        if (FlymeUtils.isNotEmpty(imHander)) {
            //注册即时通讯用户
            appUser.setUserToken(imHander.register(userId.toString(), appUser.getNickName(), appUser.getAvatar()));
        }
    }


    @Override
    public ResultBody updateMineInfo(Map params) {
        ResultBody resultBody;
        Long userId = OpenHelper.getUserId();
        CriteriaUpdate<AppUser> cu = new CriteriaUpdate(params, AppUser.class);
        AppUser appUser = cu.getEntity(AppUser.class);
        AppUser old = getById(userId);
        Long proId = FlymeUtils.getLong(appUser.getProId(), old.getProId());
        Long cityId = FlymeUtils.getLong(appUser.getCityId(), old.getCityId());
        Long areaId = FlymeUtils.getLong(appUser.getAreaId(), old.getAreaId());
        String areaName = areaService.getAreaName(proId, cityId, areaId);
        String avatar = FlymeUtils.getString(appUser.getAvatar(), old.getAvatar());
        String nickName = FlymeUtils.getString(appUser.getNickName(), FlymeUtils.getString(old.getNickName(), "游客" + RandomUtil.randomNumbers(5)));
        if (FlymeUtils.isEmpty(avatar)) {
            appUser.setAvatar(CommonConstants.DEFAULT_USERHEAD);
        }
        appUser.setAreaId(areaId);
        appUser.setCityId(cityId);
        appUser.setAreaName(areaName);
        appUser.setProId(proId);
        appUser.setUserId(userId);
        cu.eq(true, "userId", userId);
        cu.setIdValue(userId);
        if (FlymeUtils.isNotEmpty(imHander)) {
            String userToken = old.getUserToken();
            if (FlymeUtils.isNotEmpty(userToken)) {
                //刷新即时通讯用户
                imHander.userRefresh(userId.toString(), nickName, avatar);
            } else {
                appUser.setUserToken(imHander.register(userId.toString(), nickName, avatar));
            }
        }
        resultBody = baseEdit(cu, appUser);
        OpenUser openUser = OpenHelper.getUser();
        if (FlymeUtils.isNotEmpty(appUser) && FlymeUtils.isNotEmpty(appUser.getUserName())) {
            openUser.setUsername(appUser.getUserName());
            OpenHelper.updateOpenUser(redisTokenStore, openUser);
        }
        return resultBody;
    }





    @Override
    public ResultBody setStatus(Long userId) {
        ResultBody resultBody = new ResultBody();
        AppUser user = appUserMapper.selectById(userId);
        Integer stateEnum = user.getStatus();
        CriteriaUpdate cu = new CriteriaUpdate();
        cu.eq("userId", userId);
        if (stateEnum.equals(StateEnum.ENABLE.getCode())) {
            cu.set(true, "status", StateEnum.DISABLE.getCode());
            resultBody.setMsg(StateEnum.DISABLE.getName() + "成功").data(0);
        } else {
            cu.set(true, "status", StateEnum.ENABLE);
            resultBody.setMsg(StateEnum.ENABLE.getName() + "成功").data(1);
        }
        update(cu);
        return resultBody;

    }



    @Override
    public ResultBody beforeDelete(CriteriaDelete<AppUser> cd) {
        Long userId = OpenHelper.getUserId();
        Long[] userIds = cd.getIds();
        if (FlymeUtils.contains(userIds, userId)) {
            return ResultBody.failed("禁止删除当前用户");
        }
        return super.beforeDelete(cd);
    }

    @Override
    public ResultBody afterDelete(CriteriaDelete cd, Long[] ids) {
        appAccountService.removeAccount(ids);
        return super.afterDelete(cd, ids);
    }

    @Override
    public ResultBody beforeEdit(CriteriaUpdate<AppUser> cu, AppUser appUser,EntityMap extra) {
        String areaName = areaService.getAreaName(appUser.getProId(), appUser.getCityId(), appUser.getAreaId());
        appUser.setAreaName(areaName);
        return ResultBody.ok();
    }

    @Override
    public Boolean deleteByCompanyId(Long companyId) {
        CriteriaQuery cq = new CriteriaQuery(AppUser.class);
        cq.eq(true, "companyId", companyId);
        List<AppUser> userList = list(cq);
        if (FlymeUtils.isNotEmpty(userList)) {
            for (AppUser appUser : userList) {
                appUser.setCompanyId(null);
                updateById(appUser);
            }
        }
        return true;
    }

    @Override
    public ResultBody totalUserGroupMonth(Map params) {
        CriteriaQuery cq = new CriteriaQuery(params, AppUser.class);
        cq.select("MONTH(createTime) userMonth", "count(userId) userCount");
        cq.eq("YEAR(createTime)", DateUtils.getYear());
        cq.groupBy("userMonth");
        cq.orderByAsc("createTime");
        List<Map<String, Object>> list = listMaps(cq);
        //用户新增数量
        List<Map<String, Object>> countList = new ArrayList<>();
        if (FlymeUtils.isNotEmpty(list)) {
            for (int i = 1; i <= 12; i++) {
                Map<String, Object> countMap = new HashMap<>();
                countMap.put("x", i + "月");
                countMap.put("y", 0);
                for (Map<String, Object> map : list) {
                    String userCount = map.get("userCount").toString();
                    int month = FlymeUtils.getInteger(map.get("userMonth").toString(), 0);
                    if (month == i) {
                        countMap.put("y", Integer.parseInt(userCount));
                    }
                }
                countList.add(countMap);
            }
        }
        Map<String, Object> result = new HashMap<>();
        result.put("countList", countList);
        return ResultBody.ok(result);
    }

}
